package ru.urfc.portal.ui.client.descriptor;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ru.urfc.portal.ui.client.Style;
import ru.urfc.portal.ui.client.Utils;
import ru.urfc.portal.ui.client.model.catalogs.CatalogItemWithStyle;
import ru.urfc.portal.ui.client.model.catalogs.booleans.BooleanCatalogItem;
import ru.urfc.portal.ui.client.renderers.BaseRenderer;
import ru.urfc.portal.ui.client.renderers.BooleanCatalogItemRenderer;
import ru.urfc.portal.ui.client.renderers.BooleanRenderer;
import ru.urfc.portal.ui.client.renderers.CatalogItemRenderer;
import ru.urfc.portal.ui.client.renderers.DateRenderer;
import ru.urfc.portal.ui.client.renderers.DoubleRenderer;
import ru.urfc.portal.ui.client.renderers.ExtFieldRenderer;
import ru.urfc.portal.ui.client.renderers.ExtInverseFieldRenderer;
import ru.urfc.portal.ui.client.renderers.ExtSelectFieldRenderer;
import ru.urfc.portal.ui.client.renderers.IntegerRenderer;
import ru.urfc.portal.ui.client.renderers.TextRenderer;
import ru.urfc.portal.ui.client.renderers.UrlRenderer;

import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelType;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.extjs.gxt.ui.client.widget.form.DateField;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.form.TimeField;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;

public class GridDescriptor {
	private ModelType modelType = new ModelType();
	private List<GridField> assignedFields = new ArrayList<GridField>();
	private List<GridField> editableFields = new ArrayList<GridField>();
	private List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
	private String[] restUrl;
	private Map<String, String[]> specialRestUrls = new HashMap<String, String[]>();
	private String addFieldPathPrefix = "";
	private int fieldsCount = 0;
	
	public GridDescriptor(String recordName, String ... restUrl) {
		this.modelType.setRecordName(recordName);
		this.restUrl = restUrl;
	}

	public ModelType getModelType() {
		return modelType;
	}

	public ColumnModel getColumnModel() {
		return new ColumnModel(columns);
	}

	public GridField addField(String fieldMap) {
		return addField(addFieldPathPrefix + fieldMap, null, null);
	}

	public GridField addField(String fieldMap, Class<?> type) {
		type = type == null || type.equals(String.class)? null: type; 
		return addField(addFieldPathPrefix + fieldMap, type, null);
	}

	public GridField addDateField(String fieldMap) {
		return addField(addFieldPathPrefix + fieldMap, Date.class, null);
	}
			
	public GridField addTimeField(String fieldMap) {
		return addField(addFieldPathPrefix + fieldMap, Date.class, Utils.TIME_FORMAT);
	}
			
	public GridField addField(String fieldMap, Class<?> type, String format) {
		return addField(new SimpleGridField(fieldMap, type, format));
	}
	
	public <T extends GridField> T addField(T gridField) {
		assert(modelType.getRecordName() != null);
		String fieldName = "field" + fieldsCount;
		fieldsCount += 1;
		gridField.init(fieldName, this, modelType);
		return gridField;
	}
	
	/**
	 * For use from {@link GridField#addColumn(ColumnConfig)} only
	 * @param field
	 * @param columnConfig
	 */
	void addColumn(GridField field, ColumnConfig columnConfig) {
		columnConfig.setId(field.getName());
		columns.add(columnConfig);
	}

	public String getRestUrl() {
		return Utils.createRestUrl(restUrl);
	}
	
	public String getRestUrl(String method) {
		if (specialRestUrls.containsKey(method)) {
			return Utils.createRestUrl(specialRestUrls.get(method));
		}
		return getRestUrl();
	}
	
	public void setSpecialRestUrl(String method, String ... restUrl) {
		specialRestUrls.put(method, restUrl);
	}
	
	public void setAddFieldPathPrefix(String pathPrefix) {
		this.addFieldPathPrefix = ("".equals(pathPrefix) || pathPrefix.endsWith("/"))?
				pathPrefix: pathPrefix + "/";  
	}

	String getAddFieldPathPrefix() {
		return addFieldPathPrefix;
	}
	
	public List<GridField> getAssignedFields() {
		return assignedFields;
	}
	
	public List<GridField> getEditableFields() {
		return editableFields;
	}

	public void setEditable(GridField gridField) {
		editableFields.add(gridField);
	}
	
	public GridField assign(BaseRenderer renderer) {
		GridField field = renderer.getField();
		field.assign(renderer);
		assignedFields.add(field);
		return field;
	}	

	public GridField addTextField(String fieldMap, Field<?> extField) {
		GridField field = addField(fieldMap, null).setEditable();
		return assign(new ExtFieldRenderer(field, extField));
	}

	public GridField addDateField(String fieldMap, DateField extField) {
		GridField field = addDateField(fieldMap).setEditable();
		return assign(new ExtFieldRenderer(field, extField));
	}

	public GridField addTimeField(String fieldMap, TimeField extField) {
		GridField field = addTimeField(fieldMap).setEditable();
		return assign(new ExtFieldRenderer(field, extField));
	}

	public GridField addBoolField(String fieldMap, Field<?> extField) {
		GridField field = addField(fieldMap, Boolean.class).setEditable();
		return assign(new ExtFieldRenderer(field, extField));
	}

	public GridField addBoolInverseField(String fieldMap, Field<?> extField) {
		GridField field = addField(fieldMap, Boolean.class).setEditable();
		return assign(new ExtInverseFieldRenderer(field, extField));
	}

	public GridField addSelectField(String fieldMap, ComboBox<ModelData> extField) {
		GridField field = addField(fieldMap).setEditable();
		return assign(new ExtSelectFieldRenderer(field, extField));
	}

	public GridField addTextField(String fieldMap, Element element) {
		GridField field = addField(fieldMap);
		return assign(new TextRenderer(field, element));
	}

	public GridField addIntField(String fieldMap, Element element) {
		GridField field = addField(fieldMap, Long.class);
		return assign(new IntegerRenderer(field, element));
	}

	public GridField addDateField(String fieldMap, Element element) {
		GridField field = addDateField(fieldMap);
		return assign(new DateRenderer(field, element));
	}

	public GridField addBoolField(String fieldMap, Element element, String trueValue, String falseValue) {
		GridField field = addField(fieldMap, Boolean.class);
		return assign(new BooleanRenderer(field, element, trueValue, falseValue));
	}
	
	public GridField addDblField(String fieldMap, Element element) {
		GridField field = addField(fieldMap, Double.class);
		return assign(new DoubleRenderer(field, element));
	}

	public GridField addMoneyField(String fieldMap, Element element) {
		GridField field = addDblField(fieldMap, element);
		field.getDblRenderer().setMoneyFormat();
		return field;
	}	
	
	public GridField addBoolCIField(String boolFieldMap,
			Class<? extends BooleanCatalogItem> klass, Element element, Style style) {
		GridField field = addField(boolFieldMap, Boolean.class);
		return assign(new BooleanCatalogItemRenderer(field, klass, element, style));
	}
	
	public GridField addCIFields(String textFieldMap, String codeFieldMap,
			Class<? extends CatalogItemWithStyle> klass, Element element, Style style) {
		GridField textField = addField(textFieldMap);
		GridField codeField = addField(codeFieldMap);
		return assign(new CatalogItemRenderer(textField, codeField, klass, element, style));
	}
	
	public GridField addAnchorFields(String textFieldMap, String idFieldMap,
			String urlTemplate, AnchorElement anchorElement, Element textElement) {
		GridField textField = addField(textFieldMap);
		return addAnchorFields(textField, idFieldMap, urlTemplate, anchorElement, textElement);
	}

	public GridField addAnchorFields(GridField textField, String idFieldMap,
			String urlTemplate, AnchorElement anchorElement, Element textElement) {
		GridField idField = addField(idFieldMap);
		return assign(new UrlRenderer(textField, idField, urlTemplate, anchorElement, textElement));
	}

	public CompositeGridField addCompositeField(String ... fieldMaps) {
		CompositeGridField result = new CompositeGridField();
		for (String fieldMap: fieldMaps) {
			GridField gridField = addField(fieldMap);
			result.addField(gridField);
		}
		return addField(result);
	}
	
	public GridField addTextColumn(String fieldMap, String columnTitle, int columnWidth) {
		GridField field = addTextField(fieldMap, (Element) null);
		return addFieldColumn(field, columnTitle, columnWidth);
	}

	public GridField addIntColumn(String fieldMap, String columnTitle, int columnWidth) {
		GridField field = addIntField(fieldMap, null);
		return addFieldColumnRight(field, columnTitle, columnWidth);
	}

	public GridField addDateColumn(String fieldMap, String columnTitle, int columnWidth) {
		GridField field = addDateField(fieldMap, (Element)null);
		return addFieldColumnRight(field, columnTitle, columnWidth);
	}

	public GridField addMoneyColumn(String fieldMap, String columnTitle, int columnWidth) {
		GridField field = addMoneyField(fieldMap, null);
		return addFieldColumnRight(field, columnTitle, columnWidth);
	}

	public GridField addAnchorColumn(String textFieldMap, String idFieldMap, String urlTemplate,
			String columnTitle, int columnWidth) {
		GridField field = addAnchorFields(textFieldMap, idFieldMap, urlTemplate, null, null);
		return addFieldColumn(field, columnTitle, columnWidth);
	}

	public GridField addAnchorColumn(CompositeGridField textField, String idFieldMap, String urlTemplate,
			String columnTitle, int columnWidth) {
		GridField field = addAnchorFields(textField, idFieldMap, urlTemplate, null, null);
		return addFieldColumn(field, columnTitle, columnWidth);
	}

	public GridField addCIColumn(String textFieldMap, String codeFieldMap,
			Class<? extends CatalogItemWithStyle> klass, String columnTitle, int columnWidth) {
		GridField field = addCIFields(textFieldMap, codeFieldMap, klass, null, null);
		return addFieldColumnCenter(field, columnTitle, columnWidth);
	}

	public GridField addBoolCIColumn(String boolFieldMap, 
			Class<? extends BooleanCatalogItem> klass, String columnTitle, int columnWidth) {
		GridField field = addBoolCIField(boolFieldMap, klass, null, null);
		return addFieldColumnCenter(field, columnTitle, columnWidth);
	}
	
	// just shortcut	
	public GridField addFieldColumn(GridField field, String columnTitle, int columnWidth) {
		field.addColumn(columnTitle, columnWidth);
		return field;
	}

	public GridField addFieldColumnRight(GridField field, String columnTitle, int columnWidth) {
		field.addColumn(columnTitle, columnWidth).setAlignment(HorizontalAlignment.RIGHT);
		return field;
	}
	
	public GridField addFieldColumnCenter(GridField field, String columnTitle, int columnWidth) {
		field.addColumn(columnTitle, columnWidth).setAlignment(HorizontalAlignment.CENTER);
		return field;
	}
	
}
